/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package hormigas_packettracker.colonia;

import hormigas_packettracker.beans.Mapa;
import hormigas_packettracker.beans.Punto;
import java.util.*;
import hormigas_packettracker.beans.Vehiculo;

/**
 *
 * @author Fernando
 */
public class Hormiga {

    private boolean _done = false;
    private Nodo _nodoInicial;
    private int _capacidadInicial;
    private int _capacidadActual;
    private ArrayList<Nodo> _route;
    private float _speed;
    private float _combustibleIni;
    private float _combustibleAct;
    private float _fuelConsumedPerKm = (float)1;
    private static final float Q = (float)0.9;
    private static final float BETA = 1;

    public Hormiga(Vehiculo v) {
        this._capacidadInicial = v.getCapacidadActual();
        this._capacidadActual = v.getCapacidadActual();

        //this._initialFuel = v.getCombustibleActual();
        //this._currentFuel = v.getCombustibleActual();
        Punto p = v.getPosicionActual();
        this._nodoInicial = ColoniaHormigas.mapaNodos.get(Mapa.getIndicePunto(p.getX(),p.getY()));
        this._route = new ArrayList<>();
        this._route.add(_nodoInicial);
        this._speed = v.getVelocidad();
        this._done = false;
    }

    public boolean isDone() {
        return _done;
    }

    private float calcularImportanciaCamino(Nodo node, MatrizFeromona feronomas) {//según la feromona
        return feronomas.getLevel(_route.get(_route.size() - 1), node);
    }

    public ArrayList<Nodo> getRoute() {
        return _route;
    }

    private float calculateTrailAtractiveness(Nodo node) {
        //debería calcularse la distancia mas corta(usar Dijkstra? o A*?)
        //por ahora se usa la ditancia geométrica como heuristica
        //tambien se debería considerar la prioridad del paquete <- done

        float distanceFactor = Nodo.geomDistance(node, _route.get(_route.size() - 1));
        float fuelFactor = distanceFactor * _fuelConsumedPerKm;
        float timeFactor = node.getVentanaTiempoAct()- distanceFactor / _speed;
        float priorityFactor = node.getVentanaTiempoAct();
        float capacityFactor = node.getDemand();

        return 1 / distanceFactor * 1 / fuelFactor /* 1 / timeFactor * 1 / priorityFactor
                * */*1 / capacityFactor;
    }

    private boolean isPossible(Nodo nextNodo) {/*
        boolean capacityConstraint = (nextNodo.getDemand() <= this._capacidadActual);
        float distance = Nodo.geomDistance(nextNodo, _route.get(_route.size() - 1));
        float time = distance / _speed;
        boolean timeConstraint = (nextNodo.getVentanaTiempoAct() >= time);
        Nodo almacenNodo = new Nodo(Mapa.getAlmacen());
        float distanceToDepot = Nodo.geomDistance(nextNodo, almacenNodo);
        boolean fuelConstraint = ((distanceToDepot+distance) * _fuelConsumedPerKm < _combustibleAct);

        return capacityConstraint && /*fuelConstraint && timeConstraint;*/
        return true;
    }

    public float calculateCostoRuta() {
        //falta calcular el costo de forma mas precisa
        float cost = 0;
        if (_route.size() == 2 && _route.get(0) == _route.get(1))
            return Float.POSITIVE_INFINITY;
        for (int i = 1; i < _route.size() ; i++) {
            cost += Nodo.geomDistance(_route.get(i - 1), _route.get(i));
        }

        return cost;
    }

    public void selectNextDestination(ArrayList<Nodo> orders, MatrizFeromona feronomas) {
        if(this.isDone())return;
        float sum =0;
        int tam = orders.size();
        Nodo selected = null;
        float max =0;
        int maxIndex =0;
        
        ArrayList<Float> probAcum = new ArrayList<Float>(); 
        probAcum.add(sum);
        for(int i =0; i< tam ;i++ ){
            Nodo nextNodo = orders.get(i);
            if(!isPossible(nextNodo)){
                probAcum.add(sum);
                continue;
            }
            //que tan frecuente se ha pasado por esa opcion por otras hormigas?
            float t = calcularImportanciaCamino(nextNodo, feronomas);
            //que tan atractiva es la solucion en terminos de distancia, tiempo y combustible?
            float n = calculateTrailAtractiveness(nextNodo);
            float product = (float) (t * Math.pow(n, BETA));
            sum += product;
            probAcum.add(sum);
            if(product > max){
                max = product;
                maxIndex = i;
            }            
        }
        if(probAcum.get(probAcum.size()-1)==0){
            this._done=true;
            return;
        }
        if(Math.random() <= Q){
            selected = orders.get(maxIndex);
        }
        else{
            float p = (float)(Math.random()*probAcum.get(probAcum.size()-1));
            for (int i = 0; i < probAcum.size(); i++) {
                if (p <= probAcum.get(i)) {
                    selected = orders.get(i-1);
                    break;
                }
            }
        }
        if(selected == null){
            selected = orders.get(maxIndex);
        }
        this._combustibleAct -= Nodo.geomDistance(selected, this._route.get(this._route.size()-1)) * this._fuelConsumedPerKm;
        //actualizar capacidad
        if(this._capacidadActual < selected.getDemand()){
            this._capacidadActual = 0;
            this._done=true;
        }
        else{
            this._capacidadActual-=selected.getDemand();
        }
        this._route.add(selected);
        orders.remove(selected);      
    }

    public Nodo getNodoInicial() {
        return _nodoInicial;
    }

    public void setNodoInicial(Nodo _nodoInicial) {
        this._nodoInicial = _nodoInicial;
    }

    public int getCapacidadInicial() {
        return _capacidadInicial;
    }

    public void setCapacidadInicial(int _capacidadInicial) {
        this._capacidadInicial = _capacidadInicial;
    }

    public int getCapacidadActual() {
        return _capacidadActual;
    }

    public void setCapacidadActual(int _capacidadActual) {
        this._capacidadActual = _capacidadActual;
    }

    public float getSpeed() {
        return _speed;
    }

    public void setSpeed(float _speed) {
        this._speed = _speed;
    }

    public float getCombustibleIni() {
        return _combustibleIni;
    }

    public void setCombustibleIni(float _combustibleIni) {
        this._combustibleIni = _combustibleIni;
    }

    public float getCombustibleAct() {
        return _combustibleAct;
    }

    public void setCombustibleAct(float _combustibleAct) {
        this._combustibleAct = _combustibleAct;
    }

    public float getFuelConsumedPerKm() {
        return _fuelConsumedPerKm;
    }

    public void setFuelConsumedPerKm(float _fuelConsumedPerKm) {
        this._fuelConsumedPerKm = _fuelConsumedPerKm;
    }
    
}
